<?php
	/**
	 * <version greek="beta" major="1" minor="0">Cochin Splash Hen</version>
	 * <author email="njones@art-k-tec.com">Nika Jones</author>
	 * <copyright start="2007">Art-k-tec Systems, LLC</copyright>
	 * <license type="Apache2.0" href="http://www.opensource.org/licenses/apache2.0.php">Apache License, Version 2.0</license>
	 */
	class bQueueException extends Exception {
		const INVALID_PRIORITY =0;
		const INVALID_QUEUE_TYPE =0;
	}
	
	class PriorityIterator implements Iterator {
		
		private $nPriorityIndex;
		private $nPriorityKeyIndex;
		
		private $aPriority;
		private $aPriorityKeys;
		
		public function __construct($aPriority) {
			
			$this->aPriority =$aPriority;
			$this->aPriorityKeys =array_keys($aPriority);
			$this->nPriorityKeyIndex =0;
			$this->nPriorityIndex =0;
		}
		
		public function current() {
		
			$sPriority =$this->aPriorityKeys[$this->nPriorityKeyIndex];
			return $this->aPriority[$sPriority][$this->nPriorityIndex];
		}
		
		public function key() {
			
			return $this->nPriorityKeyIndex;
		}
		
		public function next() {
		
			$this->nPriorityIndex++;
			$sPriority =$this->aPriorityKeys[$this->nPriorityKeyIndex];
			
			if(!isset($this->aPriority[$sPriority][$this->nPriorityIndex])) {
				
				$this->nPriorityKeyIndex++;
				$this->nPriorityIndex =0;
			}
		}
		
		public function rewind() {
			
			$this->nPriorityKeyIndex =0;
			$this->nPriorityIndex =0;
		}
		
		public function valid() {
			
			$sPriority =$this->aPriorityKeys[$this->nPriorityKeyIndex];
			return isset($this->aPriority[$sPriority][$this->nPriorityIndex]);
		}
  	}
	
	/// <summary>
	///  A class that allows for queuing of data
	/// </summary>
	class bQueue implements IteratorAggregate, Countable {
		private static $nMajor =1;
		private static $nMinor =0;
		private static $sRevision ="0000";
		
		const FIFO 			=0;
		const FILO			=1;
		const FIFO_PRIORITY	=2; // Depreciated
		const FILO_PRIORITY	=4; // Depreciated
		const PRIORITY_ASC	=2;
		const PRIORITY_DESC	=4;
		
		const PRIORITY_LOW =0.0;
		const PRIORITY_HIGH =1.0;
		
		private $sType;
		private $aQueue;
		
		/// <summary>
		///  constructor
		/// </summary>
		public function __construct($cType =self::FIFO) {
			
			if(!is_numeric($cType) || ($cType > 4 && $cType > 0)) {
				
				throw new bQueueException(
					'You must pass a valid queue type.',
					bQueueException::INVALID_QUEUE_TYPE);
			}
			$this->aQueue =array();
			$this->sType =$cType;
		}
		
		/// <summary>
		///  Return the version of this object, mainly for package management
		/// </summary>
		public static function version() { return self::$nMajor.'.'.self::$nMinor.'.'.self::$sRevision; }
		
		/// <summary>
		///  Sets the array that will be iterated over
		/// </summary>
		public function getIterator() {

			switch($this->sType) {
				
				case self::FIFO:
					$oIterator =new ArrayIterator($this->aQueue);
				break;
				
				case self::FILO:
					
					$aQueueReverse =$this->aQueue;
					krsort($aQueueReverse);
					$oIterator =new ArrayIterator($aQueueReverse);
				break;

				case self::PRIORITY_ASC:
				
					ksort($this->aQueue);
					$oIterator =new PriorityIterator($this->aQueue);
				break;
				
				case self::PRIORITY_DESC:

					krsort($this->aQueue);
					$oIterator =new PriorityIterator($this->aQueue);
				break;
			}

			$this->aQueue =array(); // clears the queue --make sure this is the correct behaviour.
			return $oIterator;
		}
		
		/// <summary>
		///  The interface method for counting with Countable of the SPL
		/// </summary>
		public function count() {
			
			return count($this->aQueue);
		}
		
		/// <summary>
		///  Add an item to the queue
		/// </summary>
		/// <param type="mixed" name="mData">
		///  The item that is to be placed in the queue
		/// </param>
		/// <param type="int" name="nPriority" optional="true">
		///  The numeric priority of the queued item
		/// </param>
		public function EnQueue($mData, $nPriority =null) {
			
			switch($this->sType) {
				case self::FIFO:
				case self::FILO: // drop
					$this->aQueue[] =$mData;
				break;
				
				case self::PRIORITY_ASC:
				case self::PRIORITY_DESC: // drop
					if(!is_numeric($nPriority)) {
						
						throw new bQueueException(
							'The priority ['.$nPriority.'] must be a number',
							bQueueException::INVALID_PRIORITY);
					}
					
					$this->aQueue[(int)($nPriority * 10)][] =$mData; // so 0 <-> 1 priorities will work.
				break;
			}
			
			return true;
		}
		
		/// <summary>
		///  Remove the item from the queue
		/// </summary>
		public function DeQueue() {
			$mReturn =false;
			
			switch($this->sType) {
				
				case self::FIFO:
					$mReturn =array_shift($this->aQueue);
				break;
				
				case self::FILO:
					$mReturn =array_pop($this->aQueue);
				break;
				
				case self::PRIORITY_ASC:
					
					ksort($this->aQueue);
					$nTopPriority =key($this->aQueue);
					$mReturn =array_shift($this->aQueue[$nTopPriority]);
					
					if(count($this->aQueue[$nTopPriority]) ==0) { unset($this->aQueue[$nTopPriority]); }
				break;
				
				case self::PRIORITY_DESC:
				
					krsort($this->aQueue);
					$nTopPriority =key($this->aQueue);
					$mReturn =array_shift($this->aQueue[$nTopPriority]);
					
					if(count($this->aQueue[$nTopPriority]) ==0) { unset($this->aQueue[$nTopPriority]); }
				break;
			}
			
			return $mReturn;
		}
		
		/// <summary>
		///  Look at the next item in the queue without taking it out
		///  of the queue
		/// </summary>
		public function Peek() {
			
			switch($this->sType) {
				
				case self::FIFO:
					$mReturn =current($this->aQueue);
				break;
				
				case self::FILO:
					$mReturn =end($this->aQueue);
				break;
				
				case self::PRIORITY_ASC:
					
					ksort($this->aQueue);
					$nTopPriority =key($this->aQueue);
					$mReturn =current($this->aQueue[$nTopPriority]);
				break;
				
				case self::PRIORITY_DESC:
					
					krsort($this->aQueue);
					$nTopPriority =key($this->aQueue);
					$mReturn =end($this->aQueue[$nTopPriority]);
				break;
			}

			return $mReturn;
		}
		
		/// <summary>
		///  Removes all of the items from the queue and starts fresh
		/// <summary>
		public function Clear() {
			
			$this->aQueue =array();
		}
	}
?>